﻿using System;
using System.Linq;
using DLLx;
using RayDen.Library.Components.SystemComponents;
using RayDen.RayEngine.Core.Types;
using RayDen.RayEngine.Data;

using BBox = RayDen.RayEngine.IntersectionEngines.BoundingVolumeHierarchyAccelerationStructure.BBox;

namespace RayDen.RayEngine.IntersectionEngines {

    public class BvhAccelHelper {
        private BvhDataAdapter da;

        private GpuSmitsBVHNode[] bvhData;



        public BvhAccelHelper(RayEngineScene scn) {
            this.da = new BvhDataAdapter(scn);
            this.bvhData = this.da.BuildData().ToArray();

            int leafc = this.bvhData.Count(i => i.primitive != RayBuffer.NotHit);
            int branches = this.bvhData.Length - leafc;
            Tracer.TraceLine("Leafs {0} Branches {1}", leafc, branches);
        }


 

        public static bool IntersectBox(ref BBox box, ref RayData ray) {
            float t0 = ray.Min;
            float t1 = ray.Max;

            var rayInvDir = ray.InvDirection;
            //1f /ray.Dir;

            float invDirX = rayInvDir.x;
            if (!process_box_coord(ref t0, ref t1, box.Min.x - ray.Org.x, box.Max.x - ray.Org.x, invDirX))
                return false;

            float invDirY = rayInvDir.y;
            if (!process_box_coord(ref t0, ref t1, box.Min.y - ray.Org.y, box.Max.y - ray.Org.y, invDirY))
                return false;

            float invDirZ = rayInvDir.z;
            if (!process_box_coord(ref t0, ref t1, box.Min.z - ray.Org.z, box.Max.z - ray.Org.z, invDirZ))
                return false;
            return true;
        }

        private static bool process_box_coord(ref float t0, ref float t1, float mnRO, float mxRO, float invRayDir) {
            float tNear = (mnRO) * invRayDir;
            float tFar = (mxRO) * invRayDir;
            // Update parametric interval from slab intersection $t$s
            if (tNear > tFar)
                swap(ref tNear, ref tFar);
            t0 = tNear > t0 ? tNear : t0;
            t1 = tFar < t1 ? tFar : t1;
            if (t0 > t1)
                return false;
            return true;
        }
        private static void swap(ref float tNear, ref  float tFar) {
            float tmp = tNear;
            tNear = tFar;
            tFar = tmp;
        }
    }
}
